package org.NooLab.glue.net.http.rest;

import java.util.Arrays;
import java.util.Vector;

import org.NooLab.glue.components.DataSenderIntf;
 
import org.NooLab.glue.net.UserData;
import org.NooLab.utilities.logging.PrintLog;
import org.NooLab.utilities.net.UrlUtilities;
import org.restlet.data.ChallengeResponse;
import org.restlet.data.ChallengeScheme;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.resource.ClientResource;
import org.restlet.resource.ResourceException;

import org.NooLab.glue.msgframe.*;


public class HttpRestletSender  implements DataSenderIntf{

	String resourceURI ="" ;
	ClientResource glueResource;
	
	HttpResponseHandlerIntf responseDigester;
	
	UserData basicuser;
	
	String role;
	
	String[] httpmet = new String[]{"get","post","put","delete","trace","option"} ;
	Vector<String> httpmethods ;
	
	
	String xmlheader ="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
	
	int lastSenderState=0;
	
	PrintLog out = new PrintLog(4,true) ;
	XMessage xmsg ;
	
	
	public HttpRestletSender( String resourceurl,
							  String role,	
							  HttpResponseHandlerIntf responsedigester,
							  PrintLog outprn){
	
		resourceURI = resourceurl;
		responseDigester = responsedigester;
		this.role = role;
		
		basicuser = new UserData();
		basicuser.setName("noolab");
		basicuser.setPassword("glue") ;
		
		// responseDigester.setHttpResponse("");
		httpmethods = new Vector<String>(Arrays.asList(httpmet)) ;
		
		if (outprn!=null){
			out = outprn;
		}
		xmsg = new XMessage (out)  ;
		xmsg.getSpecifiedInfo(xmlheader+"<t value=\"xyz\">a</t>", "//t", "value") ;
	}
	
	@Override
	public int getLastState(String suid) {
		
		return lastSenderState;
	}

	@Override
	public void send(Object obj) {
		 
		send( obj,"");
	}
	/**
	 * the object contains also information about the sending mode !!
	 * i.e. the String httpCmd like POST,PUT,GET ...
	 * 
	 * 
	 */
	@Override
	public void send(Object obj, String suid) {
		
		int p1,p2,p3,httpMethodIndex=-1;
		StringRepresentation xmlStrRepresentation ;
		String xmlStr,rawstr, str, metainfo = "", methodstr = "",addrstr="";
		Representation responseRepr=null ; 
		String role,routeStr="", actionStr, waitStr="",targetUrlStr="" ;
		
		
		try {
			lastSenderState = 0; // actually, this has to be tied to "suid" !
			
			str = (String)obj; rawstr=str;
			// get routing info from object, and extract the actual xml part 
			p1 = str.indexOf("::") ;
			p2 = str.indexOf("<meta>") ;
			p3 = str.indexOf("<?xml") ;
			
			if ((p1>0) && (p2>=0) && (p1<p3)){
				metainfo = xmlheader + str.substring(0, p1) ;
				str = str.substring(p1+2, str.length()).trim() ;
				
				// <metainfo><method name="post|put|get"></metainfo>
				// TODO use match routine from other message digesters 
				xmsg.setContentRoot("meta");
				
				routeStr = xmsg.getSpecifiedInfo(metainfo, "/route", "value");
				actionStr = xmsg.getSpecifiedInfo(metainfo, "/action", "value");
				targetUrlStr = xmsg.getSpecifiedInfo(metainfo, "/host", "addr");
				
				if (targetUrlStr.length()>0){
					// exchanging hosts in address ...
					// addrstr = UrlUtilities.exchangeHostsInAddress( resourceURI, "", targetUrlStr);
					if (addrstr.length()>0){
						// resourceURI = addrstr;
					}
				}
				
				xmlStr = str;
				
				// still some :: ? final cleaning from heading info
				p1 = str.indexOf("::") ;
				p3 = str.indexOf("<?xml") ;
				
				if ((p1>0) && (p3>p1)){
					xmlStr = xmlStr.substring(p1+2, xmlStr.length()) ;
				}
				
				String sendAddrStr = UrlUtilities.getDomainFromPageAddr(resourceURI) ;
				if ((resourceURI.length()>0) && (sendAddrStr.length()==0)){
					throw(new Exception("URL error, no send will take place."));
					// return ;
				}
				if (xmlStr.contains("$checkaddress$")){
					String tagstr = "<targetaddress  value=\""+sendAddrStr+"\" />";
					xmlStr = xmlStr.replace("$checkaddress$", tagstr) ;
				}
				
				// -------------------------------------------------------------------------------
				
				if (( actionStr.toLowerCase().contentEquals("get")) ||
				 	(actionStr.length()==0)){
					methodstr = "get" ;
				}
						
				if ((actionStr.toLowerCase().contentEquals("post")) || ( routeStr.contains("register") )){ // and "type = "request"
					methodstr = "post" ;
				}

				if (  (actionStr.contains("new")) || (actionStr.toLowerCase().contentEquals("put"))){ // and "type = "request"
					methodstr = "put" ;
				}
			  
				
				httpMethodIndex = httpmethods.indexOf(methodstr) ;
				str = xmlStr ;
			}
			
			xmlStr = str;
			// is it an explicit send operation ?
			if ( (httpMethodIndex>=0) && (methodstr.length()>0))  {
				
				if (routeStr.startsWith("/")==false){
					routeStr = "/"+routeStr;
				}
				// create resource
				glueResource = new ClientResource(resourceURI+routeStr);
												out.print(5, "HttprestletSender: Method identified, sending to URL = "+(resourceURI+routeStr)) ;
				
				insertAuthentication(glueResource);
				 
				//

				xmlStrRepresentation = new StringRepresentation(xmlStr);
				xmlStrRepresentation.setMediaType(MediaType.TEXT_XML);

				if (httpMethodIndex == 0) {
					responseRepr = glueResource.get();
				}
				if (httpMethodIndex == 1) {
					responseRepr = glueResource.post(xmlStrRepresentation);
				}
				if (httpMethodIndex == 2) {
					responseRepr = glueResource.put(xmlStrRepresentation);
				}
				if (httpMethodIndex == 3) {
					// we will use put for delete requests on the server
					responseRepr = glueResource.delete();
				}

				
				// probably not here, but from a remote class ???
				// out.print(2, "\nResponse : " + str + "\n");
				role = "";
				responseDigester.setHttpResponse(role, responseRepr); // implemented in RestletBox
				// callback to RestletBox...
				 
				glueResource.release(); 
				
			} else{
				// or an implicit sending, in case http and within session ?
				
				
			}
		}catch (Exception e) {
			lastSenderState = __SEND_ERR_ERROR ;
			
			handlingResourceException( e , "send() -> " + resourceURI);
		}

	}

	@Override
	public void release() {
		 
		
	}

	@Override
	public void setTimeOut(int value) {
		// TODO Auto-generated method stub
		
	}
	
	
	
	/**
	 * we use a very simple and static authentication, just to disable "any" requests,
	 * for https, see the tutorial on the restlet site:
	 * http://wiki.restlet.org/docs_2.1/13-restlet/27-restlet/46-restlet/213-restlet.html
	 */
	private void insertAuthentication( ClientResource cresource){
		
		ChallengeResponse authentication ;
		ChallengeScheme scheme ;
		
		scheme = ChallengeScheme.HTTP_BASIC;

		authentication = new ChallengeResponse(scheme,basicuser.getName(), basicuser.getPassword());
		cresource.setChallengeResponse(authentication);

	}

	private void handlingResourceException( Exception ex , String referrer){
		
		ResourceException rex ; 
		
		if (ex instanceof ResourceException){
			 
			rex = (ResourceException)ex ;
			
			String errmsg = rex.getMessage() ;
			Status est = rex.getStatus();

			if (handlingKnownError(errmsg, est, referrer)){
				
			}else{
				
				System.out.println("unknown resource exception :\n");
				rex.printStackTrace();	
			}
			
		}else{
			ex.printStackTrace();
		}

		
		
	}
	
	private boolean handlingKnownError( String msg, Status status, String referrer){
		boolean rB=false;
		int code ;
		String description ;
		
		
		description = status.getDescription() ;
		 
		out.print(3,"resource exception in : "+referrer) ;
		code = status.getCode();
		out.print(3,"code : "+code);
		out.print(3,"descr: "+description);
		out.print(3,"msg  : "+msg);
		out.print(3,"------------------------------------\n") ;
		
		if (!rB){ rB = (code==1002) ;}  // connection time out 
		if (!rB){ rB = (code==1001) ;}  // Connection refused: connect, wrong port, wrong address
		if (!rB){ rB = (code==401) ;}   // Status.CLIENT_ERROR_UNAUTHORIZED
		if (!rB){ rB = (code==404) ;}  
		if (!rB){ rB = (code==405) ;}   // (405) - Method Not Allowed, e.g. client sends PUT request, but there is none
		if (!rB){ rB = (code==500) ;}   // Internal Server Error (500) - Internal Server Error
		
		// .contains("401")) || (errmsg.toLowerCase().contains("unauthorized"))){
		if (code==405){
			code=code+0;
		}
		// System.out.println("Access denied, check your credentials,"+str);
			
		return rB;
	}
	
}
